# Copyright (c) 2011, 2012 Nathan Grubb
# Available under the new BSD license; see LICENSE.txt

"""lib/message.py: Contains all IRC line parsing, contained in the Message
   object.
"""

class Message(object):
    """The Message object encapsulates an IRC line, or message. It also has the
       ability to return to it's original IRC line.

       An IRC message is composed of a prefix, command, and arguments, which
       are fields in this object. If the prefix is a hostmask, the relevant
       fields (nickname, username, and hostname) are available. You can
       determine if there is a hostmask by the is_mask boolean flag.
    """

    has_mask = False # Not applicable until the prefix has been parsed

    def __init__(self, command, args, prefix=None):
        self.command = command
        self.args = args
        self.prefix = prefix

        # Check the prefix for a hostmask
        if self.prefix:
            if "!" in self.prefix:
                # Hostmask format: nickname!~username@hostname
                self.nickname, userhost = prefix.split("!")
                self.username, self.hostname = userhost.split("@")
                self.has_mask = True

    @classmethod
    def parse(cls, line):
        """Parses a line and returns a new Message object, or if detectable,
           a more specific subclass of Message.
        """
        
        prefix = ""
        trailing = []

        if line[0] == ':':
            prefix, line = line[1:].split(' ', 1)
        if line.find(' :') != -1:
            line, trailing = line.split(' :', 1)
            args = line.split()
            args.append(trailing)
        else:
            args = line.split()
        command = args.pop(0).lower()

        if command in MESSAGE_TYPES:
            return MESSAGE_TYPES[command](command, args, prefix)
        else:
            return Message(command, args, prefix)

    def __str__(self):
        """Returns a (directly sendable) raw IRC line, built from this object's
           fields. A newline is appended.
        """

        if self.prefix:
            start = ":%s %s" % (self.prefix, self.command)
        else:
            start = self.command

        if self.args:
            args = list(self.args)
            if " " in args[-1] or ":" in args[-1] or not args[-1]:
                args[-1] = ":" + args[-1]
            return "%s %s" % (start, " ".join(args)) + "\r\n"
        else:
            return start + "\r\n"

# A list of specific messages types inheriting Message, each providing parsing and
# variable definitions to try to make sense of the message's specific arguments.
# Basically, these are meant to be used as an API for abstracting Message.

class PrivateMessage(Message):
    def __init__(self, command, args, prefix=None):
        Message.__init__(self, command, args, prefix=prefix)
        self.channel = self.args[0]
        self.message = " ".join(self.args[1:])

class JoinMessage(Message):
    def __init__(self, command, args, prefix=None):
        Message.__init__(self, command, args, prefix=prefix)
        self.channel = self.args[0]

class PartMessage(Message):
    def __init__(self, command, args, prefix=None):
        Message.__init__(self, command, args, prefix=prefix)
        self.channel = self.args[0]
        self.reason = ""

        if len(self.args) > 1:
            self.reason = self.args[1]

class TopicMessage(Message):
    def __init__(self, command, args, prefix=None):
        Message.__init__(self, command, args, prefix=prefix)
        self.channel = self.args[0]
        self.topic = self.args[1]

class ModeMessage(Message):
    def __init__(self, command, args, prefix=None):
        Message.__init__(self, command, args, prefix=prefix)
        if not self.has_mask:
             self.nickname = self.prefix

        self.channel = self.args[0]
        mode = self.args[1]

        if len(self.args) > 2:
            self.nicknames = self.args[2:]
        else:
            self.nicknames = None

        self.mode_plusminus = mode[0]
        self.mode = mode[1]

class KickMessage(Message):
    def __init__(self, command, args, prefix=None):
        Message.__init__(self, command, args, prefix=prefix)
        self.channel = self.args[0]
        self.kicked_nickname = self.args[1]
        self.reason = self.args[2]

class NickMessage(Message):
    def __init__(self, command, args, prefix=None):
        Message.__init__(self, command, args, prefix=prefix)
        self.new_nickname = self.args[0]

class QuitMessage(Message):
    def __init__(self, command, args, prefix=None):
        Message.__init__(self, command, args, prefix=prefix)
        self.reason = self.args[0]

MESSAGE_TYPES = {
    "privmsg": PrivateMessage, 
    "join": JoinMessage,
    "part": PartMessage,
    "topic": TopicMessage,
    "mode": ModeMessage,
    "kick": KickMessage,
    "nick": NickMessage,
    "quit": QuitMessage,
}